import "Turbine";
import "Turbine.Gameplay";
--import "Turbine.Utils";
import "Turbine.UI";
import "Turbine.UI.Lotro";
import "LotroLCD.LotroLCD.Utils";
import "LotroLCD.LotroLCD.UI";

-- LotroLCD is derrived from both Turbine.Plugin and Turbine.UI.Control
-- Turbine.UI.Control gives ability to receive updates
LotroLCD = class(Turbine.Plugin);
function LotroLCD:Constructor()
	Turbine.Plugin.Constructor(self);
	self.Control = class(Turbine.UI.Control);
	Turbine.UI.Control.Constructor(self.Control);
	
	-- Forward update events to the update function
	self.Control.Update = function()
		self:Update()
	end
	
	-- Subscribe to Updates, one per frame
	self.Control:SetWantsUpdates(true);
	
	-- Get Local Player
	self.player = Turbine.Gameplay.LocalPlayer.GetInstance();
	
	-- Load saved settings from file or initalize settings with default values
	self.settings = self:LoadSettings();
	
	--Get version and full plugin name, used to show version to user
	-- and stored in settings so we can update config with new versions
	local loaded_plugins = Turbine.PluginManager.GetAvailablePlugins()
	for i,v in ipairs(loaded_plugins) do
		if v.Name == "LotroLCD" then
			self.settings.version = v.Version;
			self.settings.name = v.Name;
		end
	end
	
	-- Write name and version to console when initalizing
	Turbine.Shell.WriteLine (string.format("%s v%s",self.settings.name,self.settings.version));
	
	-- Update counter, used to track intervals between updating plugin screen 
	self.updateCounter = 0;
	
	-- Initialize Last Combat Time
	self.lastCombatTime = Turbine.Engine.GetGameTime();
	
	-- Load element values, create a dictionary to hold data from file, fetch all elements the first time around because windows have not been created yet
	self.enabledElements = dictToSortedArray(mergeDict(self.settings.availableElements, self.settings.defaultMessages));
	self.loadedData = self:GetAllValues(self.enabledElements);
	
	self.windows = {};
	for i in ipairs(self.settings.windows) do
		if self.settings.windows[i].windowType == "LotroLCDWindow" then
			table.insert(self.windows, UI.LotroLCDWindow(self, self.settings.windows[i], self));
		elseif self.settings.windows[i].windowType == "LotroLCDStatsWindow" then
			table.insert(self.windows, UI.LotroLCDStatsWindow(self, self.settings.windows[i], self));
		elseif self.settings.windows[i].windowType == "LotroLCDConfigWindow" then
			table.insert(self.windows, UI.LotroLCDConfigWindow(self, self.settings.windows[i], self));
		elseif self.settings.windows[i].windowType == "LotroLCDMessageWindow" then
			table.insert(self.windows, UI.LotroLCDMessageWindow(self, self.settings.windows[i], self));
		end
	end
	
	-- Fetch a list of all elements in use, this data is only available after all windows have been loaded
	self.enabledElements = self:GetEnabledElements();
end

-- Save current settings
function LotroLCD:SaveSettings()
	-- Refresh list of enabled Elements
	self.enabledElements = self:GetEnabledElements();
	Turbine.PluginData.Save(Turbine.DataScope.Character, "LotroLCD", self.settings);
end

-- Load settings from file, or initialize with default values
function LotroLCD:LoadSettings()

	-- Touch file to indicate plugin is loaded so C code can find where to put data
	Turbine.PluginData.Save(Turbine.DataScope.Account, "LoadData", {touch = true}); 
	
	settings = Turbine.PluginData.Load(Turbine.DataScope.Character, "LotroLCD");
	if type(settings) ~= "table" then
		settings = { };
	end
	settings.availableElements = {	ctime = 1,
									dmg = 2,
									dps = 3,
									hdps = 4,
									tdmg = 5,
									tdps = 6,
									heal = 7,
									hps = 8,
									pdmg = 9,
									pdps = 10,
									spdmg = 11,
									spdps = 12,
									adwarfdmg = 0,
									beldmg = 0,
									westdmg = 0,
									commondmg = 0,
									firedmg = 0,
									lightdmg = 0,
									lightningdmg = 0,
									frostdmg = 0,
									shadowdmg = 0,
									aciddmg = 0,
									tadwarfdmg = 0,
									tbeldmg = 0,
									twestdmg = 0,
									tcommondmg = 0,
									tfiredmg = 0,
									tlightdmg = 0,
									tlightningdmg = 0,
									tfrostdmg = 0,
									tshadowdmg = 0,
									taciddmg = 0}
	
	settings.defaultMessages = {	Message0 = 1,
									Message1 = 2,
									Message2 = 3,
									Message3 = 4,
									Message4 = 5,
									Message5 = 0,
									Message6 = 0,
									Message7 = 0,
									Message8 = 0,
									Message9 = 0}
									
	settings.elementDescriptions = {	ctime = "Combat Time (seconds, since start of combat or duration of last combat)",
										dmg = "Damage (Total damage you have dealt in this or last combat)",
										dps = "Damage per Second (Average damage per second you have dealt since the start of this/last combat)",
										hdps = "History Damage per Second (Average damage per second for all combats since C plugin was started)",
										tdmg = "Taken Damage (Total damage you have taken in this/last combat)",
										tdps = "Taken Damage per Second (Average damage per second you have taken since the start of this/last combat)",
										heal = "Healed Damage (Total damage you have healed in this/last combat)",
										hps = "Healed Damage per Second (Average damage you have healed per second since the start of this/last combat)",
										pdmg = "Pet Damage (Total damage your pet has dealt in this or last combat )",
										pdps = "Pet Damage per Second (Average damage per second your pet has dealt since the start of this/last combat)",
										spdmg = "Skirmish Pet Damage (Total damage your skirmish pet has dealt in this or last combat )",
										spdps = "Skirmish Pet Damage per Second (Average damage per second your skirmish pet has dealt since the start of this/last combat)",
										adwarfdmg = "Ancient Dwarf DMG (Total Ancient Dwarf damage you have dealt in this or last combat)",
										beldmg = "Beleriand DMG (Total Beleriand damage you have dealt in this or last combat)",
										westdmg = "Westernesse DMG (Total Westernesse damage you have dealt in this or last combat)",
										commondmg = "Common DMG (Total Common damage you have dealt in this or last combat)",
										firedmg = "Fire DMG (Total Fire damage you have dealt in this or last combat)",
										lightdmg = "Light DMG (Total Light damage you have dealt in this or last combat)",
										lightningdmg = "Lightning DMG (Total Lightning damage you have dealt in this or last combat)",
										frostdmg = "Frost DMG (Total Frost damage you have dealt in this or last combat)",
										shadowdmg = "Shadow DMG (Total Shadow damage you have dealt in this or last combat)",
										aciddmg = "Acid DMG (Total Acid damage you have dealt in this or last combat)",
										tadwarfdmg = "Taken Ancient Dwarf DMG (Total Ancient Dwarf damage you have taken in this or last combat)",
										tbeldmg = "Taken Beleriand DMG (Total Beleriand damage you have taken in this or last combat)",
										twestdmg = "Taken Westernesse DMG (Total Westernesse damage you have taken in this or last combat)",
										tcommondmg = "Taken Common DMG (Total Common damage you have taken in this or last combat)",
										tfiredmg = "Taken Fire DMG (Total Fire damage you have taken in this or last combat)",
										tlightdmg = "Taken Light DMG (Total Light damage you have taken in this or last combat)",
										tlightningdmg = "Taken Lightning DMG (Total Lightning damage you have taken in this or last combat)",
										tfrostdmg = "Taken Frost DMG (Total Frost damage you have taken in this or last combat)",
										tshadowdmg = "Taken Shadow DMG (Total Shadow damage you have taken in this or last combat)",
										taciddmg = "Taken Acid DMG (Total Acid damage you have taken in this or last combat)"}

	if not settings.windows then
		settings.windows = self:GetDefaultWindowSettings();
	end
	
	-- Make sure all available data elements are represented in the users saved settings
	for i in ipairs(settings.windows) do
		if settings.windows[i].windowType == "LotroLCDStatsWindow" then
			for k,v in pairs(settings.availableElements) do
				if not settings.windows[i].elements[k] then
					settings.windows[i].elements[k] = v;
					Turbine.Shell.WriteLine (string.format("New stats element available: %s",k));
				end
			end
		elseif settings.windows[i].windowType == "LotroLCDMessageWindow" then
			for k,v in pairs(settings.defaultMessages) do
				if not settings.windows[i].elements[k] then
					settings.windows[i].elements[k] = v;
					Turbine.Shell.WriteLine (string.format("New message element available: %s",k));
				end
			end
		end
	end
	
	return settings;
end

function LotroLCD:GetDefaultWindowSettings()
	return { 	{windowType = "LotroLCDStatsWindow", windowTitle = "Stats", x = 100, y = 100, opacity = 0.8, visibility = true, updateOutOfCombat = false, elements = {}},
				{windowType = "LotroLCDMessageWindow", windowTitle = "Messages", x = 300, y = 300, opacity = 0.8, visibility = true, updateOutOfCombat = true, elements = {}}
			}
end

-- Retrieve Values for elements supplied, return array of element values in same order
function LotroLCD:GetElementValues(elementArray)
	local elementValues = {};
	for i in pairs(elementArray) do
		table.insert(elementValues, self.loadedData[elementArray[i]]);
	end
	return elementValues;
end

-- Retrieve keys for all enabled elements
function LotroLCD:GetEnabledElements()
	local sortedElementArray = {};
	for k,v in ipairs(self.windows) do
		sortedElementArray = mergeDict(sortedElementArray, v.settings.elements);
	end
	return dictToSortedArray(sortedElementArray)
end

-- Retrieve Values for All elemements and messages specified
function LotroLCD:GetAllValues(elements)
	--local sortedElementArray = dictToSortedArray(mergeDict(self.settings.availableElements, self.settings.defaultMessages));
	return twoArraysToDict(elements, self:GetValues(elements));
end

-- Retrieve Values for elemements supplied in an array
function LotroLCD:GetValues(elements)
	local commands = Turbine.Shell.GetCommands();
	local loadedValues = {}; -- A map of retrieved values
	local sortedValues = {};
	
	for k, v in pairs(commands) do
		local valueStart = string.find(v, "Value");
		if valueStart then
			loadedValues[string.sub(v, 1, valueStart -1 )] = string.sub(v, valueStart + 5);
		end
	end
	
	for i,v in ipairs(elements) do -- Return a simple array with void fields as "---"
		if loadedValues[v] then
			table.insert(sortedValues,loadedValues[v]);
		else
			table.insert(sortedValues,"---");
		end
	end
	return sortedValues;
end

-- Handle Updates
function LotroLCD:Update()
	if self.updateCounter == 20 then -- Only update plugin every Nth time a frame is drawn
		Turbine.PluginManager.LoadPlugin("LoadData");
		-- Refresh element values, create a dictionary to hold data from file
		self.loadedData = self:GetAllValues(self.enabledElements);
		for k,v in ipairs(self.windows) do
			-- Only update window if it is supposed to be updated out of combat, we are in combat or < 3 seconds since last combat ended
			-- The grace period of 3 seconds is one second less than the grace period for the C code
			if v.settings.updateOutOfCombat or self.lastCombatTime > (Turbine.Engine.GetGameTime() - 3) then
				if v.enabledElements then
					local elementValues = self:GetElementValues(v.enabledElements);
					for i in ipairs(v.enabledElements) do
						local currentItem = v.valueListbox:GetItem(i);
						currentItem:SetText(string.format("%s", elementValues[i]));
					end
				end
			end
		end
		Turbine.PluginManager.UnloadScriptState("LoadData");
		self.updateCounter = 0;
		if self.player:IsInCombat() then
			self.lastCombatTime = Turbine.Engine.GetGameTime();
		end
	end
	self.updateCounter = self.updateCounter + 1; -- Increment UpdateCounter
end